const Scope = require("./scope.js");
const walk = require("./walk.js");

/**
 * ast语法树的分析函数
 * @param {object} ast ast语法树
 * @param {MagicString} magicString magicString实例
 */
const analyse = (ast, magicString) => {
    let scope = new Scope({
        name: "global",
    }); // 当前模块的全局作用域

    // 1. 遍历当前ast语法树，分析出当前模块定义的所有全局变量
    ast.body.forEach(statement => {
        Object.defineProperties(statement, {
            // 从文件字符串中截取到我们需要的字符串
            _source: {
                value: magicString.snip(statement.start, statement.end)
            },
            // 此依赖是否已被收集
            _included: {
                value: false,
                writable: true
            },
            // 用于存放当前模块定义的所有全局变量
            _defines: {
                value: {}
            },
            // 当前作用域的所有变量名和函数名
            _dependsOn: {
                value: {}
            }
        })
        /**
         * 将变量添加到对应的作用域中
         * @param {object} declaration 变量的声明节点
         */
        const addToScope = (declaration, type) => {
            const name = declaration.id.name; // 当前函数的函数名
            scope.add(name); // 添加至全局作用域
            if(!scope.parent) {
                // 如果没有父级作用域，则是全局变量
                statement._defines[name] = true;
            }
        }

        // 深度优先遍历ast语法树
        walk(
            statement,
            {
                enter(node) {
                    let newScope; // 当前节点的新作用域
                    if (node === null || node.length === 0) return;
                    // 判断当前节点的类型
                    switch(node?.type) {
                        case "FunctionDeclaration":
                            // 1. 如果是函数节点，将此函数的函数名添加的当前作用域
                            addToScope(node);
                            // 2. 判断当前函数是否有参数
                            const params = node.params.map(param => param.name);
                            // 3. 创建新的函数作用域
                            newScope = new Scope({
                                parent: scope,
                                params,
                                name: node.id.name
                            })
                            break;
                        case "VariableDeclaration":
                            // 如果是变量节点，则将变量名添加的作用域中
                            node.declarations.forEach(addToScope);
                            break;
                        // case "Identifier":
                        //     // 1. 当前声明的名字
                        //     const name = node.name;
                        //     // 2. 在当前作用域中使用过的变量
                        //     statement._dependsOn[name] = true;
                        //     break;
                        default:
                            break;
                    }

                    // 判断当前节点是否是函数节点
                    if(newScope) {
                        // 如果是函数节点，则将函数作用域保存到当前节点的_scope属性上
                        Object.defineProperties(node, {
                            _scope: { value: newScope }
                        })
                        scope = newScope;
                    }
                },
                leave(node) {
                    // 离开节点时，如果当前节点创建了作用域，将作用域回退到上一个作用域
                    if(scope && node?._scope) {
                        scope = scope.parent;
                    }
                }
            }
        )
    })

    ast._scope = scope;

    ast.body.forEach(statement => {
        walk(
            statement,
            {
                enter(node) {
                    if (node._scope) {
                        scope = node._scope;
                    }
                    if (node.type === "Identifier") {
                        // 1. 当前声明的名字
                        const name = node.name;
                        // 2. 在当前作用域中使用过的变量
                        statement._dependsOn[name] = true;
                    }
                }
            }
        )
    })
}

module.exports = analyse;